package com.logistica.metropolitana.services;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;

import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JasperRunManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.logistica.metropolitana.converters.ActaDeEntregaConverter;
import com.logistica.metropolitana.db.daos.ActaDeEntregaDAO;
import com.logistica.metropolitana.db.model.AeConstatarSitio;
import com.logistica.metropolitana.db.model.AeEquInstalado;
import com.logistica.metropolitana.db.model.AeImagene;
import com.logistica.metropolitana.db.model.AeObservacione;
import com.logistica.metropolitana.db.model.AePaiExterno;
import com.logistica.metropolitana.db.model.AePaiInterno;
import com.logistica.metropolitana.db.model.AePrueba;
import com.logistica.metropolitana.db.model.AeSitio;
import com.logistica.metropolitana.db.model.AeVisita;
import com.logistica.metropolitana.exceptions.ActaDeEntregaException;
import com.logistica.metropolitana.utils.Utils;
import com.logistica.metropolitana.web.views.AEImagenView;
import com.logistica.metropolitana.web.views.ActaDeEntregaView;
import com.logistica.metropolitana.web.views.ConstatarSitioView;
import com.logistica.metropolitana.web.views.EquipoInstaladoView;
import com.logistica.metropolitana.web.views.PaiExternoView;
import com.logistica.metropolitana.web.views.PaiInternoView;
import com.logistica.metropolitana.web.views.PruebaView;
import com.logistica.metropolitana.web.views.VisitaView;

@Service
public class ActaDeEntregaService{

	private final Logger logger = LoggerFactory.getLogger(ActaDeEntregaService.class);

	@Autowired
	ActaDeEntregaDAO actaDeEntregaDAO;

	public List<ActaDeEntregaView> getActasDeEntrega() throws ActaDeEntregaException{
		List<ActaDeEntregaView> actas = Lists.newArrayList();
		try{
			ActaDeEntregaConverter converter = new ActaDeEntregaConverter();
			List<AeSitio> aeSitios = actaDeEntregaDAO.getActaEntregaList();

			for (AeSitio aeSitio : aeSitios) {
				actas.add(converter.convertAeSitioEntityToView(aeSitio));
			}
		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de obtener la lista de Actas de Entrega");
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_LISTS);
			throw aException;
		}
		return actas;
	}

	public ActaDeEntregaView getActaDeEntregaByGID(String gid) throws ActaDeEntregaException{
		ActaDeEntregaView view = new ActaDeEntregaView();
		ActaDeEntregaConverter conv = new ActaDeEntregaConverter();
		try{
			logger.info("Obteniendo el acta: "+gid);
			AeSitio aeSitio = actaDeEntregaDAO.getSitio(new Long(gid));
			view = conv.convertAeSitioEntityToView(aeSitio);

			List<VisitaView> visitas = Lists.newArrayList();
			if(!aeSitio.getAeVisitas().isEmpty()){
				for (AeVisita v : aeSitio.getAeVisitas()) {
					visitas.add(conv.convVisitaEntityToView(v));
				}
			}
			view.setVisitas(visitas);

			PruebaView pruebas = conv
					.convPuebaEntityToView(aeSitio.getAePrueba());
			view.setPruebas(pruebas);

			PaiInternoView pInternoView = conv
					.convPaiInternoEntityToView(aeSitio.getAePaiInterno());
			view.setPaiInternoView(pInternoView);

			PaiExternoView paiExternoView = conv
					.convPaiExternoEntityToView(aeSitio.getAePaiExterno());
			view.setPaiExternoView(paiExternoView);

			List<EquipoInstaladoView> equiposInstalados = Lists.newArrayList();
			if(!aeSitio.getAeEquInstalados().isEmpty()){
				for (AeEquInstalado eq : aeSitio.getAeEquInstalados()){
					equiposInstalados.add(conv.convEquInstEntityToView(eq));
				}
			}
			
			if(equiposInstalados.size()>0){
				view.setEquipoInstalados(ordenEquipos(equiposInstalados));
			}
			else{
				view.setEquipoInstalados(equiposInstalados);
			}

			ConstatarSitioView constatarSitioView = conv
					.convConstSitioEntityToView(aeSitio.getAeConstatarSitio());
			view.setConstatarSitioView(constatarSitioView);

			List<AEImagenView> imagenes = Lists.newArrayList();
			if(!aeSitio.getAeImagenes().isEmpty()){
				for(AeImagene img : aeSitio.getAeImagenes()){
					imagenes.add(conv.convImagenEntityToView(img));
				}
			}
			view.setImagenes(imagenes);

		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de obtener Acta de Entrega {}", gid);
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_SELECT);
			throw aException;
		}
		return view;
	}

	public List<VisitaView> getActaDeEntregaVisitasByGID(String gid) throws ActaDeEntregaException{
		List<VisitaView> visita = Lists.newArrayList();
		try{
			ActaDeEntregaConverter conv = new ActaDeEntregaConverter();
			List<AeVisita> visitas = actaDeEntregaDAO.getVisita(new Long(gid));

			for (AeVisita v : visitas) {
				visita.add(conv.convVisitaEntityToView(v));
			}
		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de obtener las Visitas del Acta de Entrega {}", gid);
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_SELECT);
			throw aException;
		}
		return visita;
	}

	public Boolean validarGidActa(String gid) throws ActaDeEntregaException{
		try{
			List<AeSitio> actas = Lists.newArrayList();
			actas = actaDeEntregaDAO.searchActaByGid(gid);
			if(actas.size()>0){
				return false;
			}
			else{
				return true;
			}
		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de validar el Gid");
			ActaDeEntregaException aException = new ActaDeEntregaException(e, ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_LISTS);
			throw aException;
		}
	}

	public List<ActaDeEntregaView> buscaActaGID(String gid) throws ActaDeEntregaException{
		try{
			List<AeSitio> actas = Lists.newArrayList();
			actas = actaDeEntregaDAO.searchActaByGid(gid);
			List<ActaDeEntregaView> av = Lists.newArrayList();
			if(actas.size()>0){
				ActaDeEntregaConverter conv = new ActaDeEntregaConverter();
				for (AeSitio aeSitio : actas) {
					av.add(conv.convertAeSitioEntityToView(aeSitio));
				}
				return av;
			}
			else{
				return null;
			}
		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de buscar el Acta de Entrega");
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_LISTS);
			throw aException;
		}	
	}

	public void addActaDeEntrega(ActaDeEntregaView view) throws ActaDeEntregaException{

		ActaDeEntregaConverter conv = new ActaDeEntregaConverter();
		try{
			AeSitio aeSitio = conv.convSitioViewToEntity(view);
			aeSitio.setEstatus("NU");
			List<AeEquInstalado> eqInstalados = Lists.newArrayList();
			if(!view.getEquipoInstalados().isEmpty()){
				for(EquipoInstaladoView v : view.getEquipoInstalados()){
					eqInstalados.add(conv.convEquInstViewToEntity(v, aeSitio, 0));
				}
			}

			aeSitio.setAeEquInstalados(eqInstalados);
			aeSitio.setAePrueba(conv.convPruebaViewToEntity(view.getPruebas(), aeSitio));
			aeSitio.setAePaiInterno(conv.convPaiIntViewToEntity(view.getPaiInternoView(), aeSitio));
			aeSitio.setAePaiExterno(conv.convPaiExtViewToEntity(view.getPaiExternoView(), aeSitio));
			aeSitio.setAeConstatarSitio(conv.convConstSitioViewToEntity(view.getConstatarSitioView(), aeSitio));

			List<AeImagene> imagenes = Lists.newArrayList();
			if(!view.getImagenes().isEmpty()){
				for(AEImagenView v : view.getImagenes()){
					imagenes.add(conv.convImagenViewToEntity(v, aeSitio));
				}
			}
			aeSitio.setAeImagenes(imagenes);

			actaDeEntregaDAO.addActaDeEntrega(aeSitio);

		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de agregar Acta de Entrega {}", view);
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_SELECT);
			throw aException;
		}
	}

	public void editActaDeEntrega(ActaDeEntregaView view) throws ActaDeEntregaException {

		ActaDeEntregaConverter conv = new ActaDeEntregaConverter();
		try{
			AeSitio aeSitio = conv.convSitioViewToEntity(view);

			List<AeEquInstalado> eqInstalados = Lists.newArrayList();
			if(!view.getEquipoInstalados().isEmpty()){
				for(EquipoInstaladoView v : view.getEquipoInstalados()){
					eqInstalados.add(conv.convEquInstViewToEntity(v, aeSitio, 1));
				}
			}
			aeSitio.setAeEquInstalados(eqInstalados);
			aeSitio.setAePrueba(conv.convPruebaViewToEntity(view.getPruebas(), aeSitio));
			aeSitio.setAePaiInterno(conv.convPaiIntViewToEntity(view.getPaiInternoView(), aeSitio));
			aeSitio.setAePaiExterno(conv.convPaiExtViewToEntity(view.getPaiExternoView(), aeSitio));
			aeSitio.setAeConstatarSitio(conv.convConstSitioViewToEntity(view.getConstatarSitioView(), aeSitio));

			List<AeImagene> imagenes = Lists.newArrayList();
			if(!view.getImagenes().isEmpty()){
				for(AEImagenView v : view.getImagenes()){
					imagenes.add(conv.convImagenViewToEntity(v,aeSitio));
				}
			}
			aeSitio.setAeImagenes(imagenes);

			actaDeEntregaDAO.editActaDeEntrega(aeSitio);

		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de editar Acta de Entrega {}", view);
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_UPDATE);
			throw aException;
		}
	}

	public Boolean addVisita(VisitaView view) throws ActaDeEntregaException{
		ActaDeEntregaConverter conv = new ActaDeEntregaConverter();
		try{
			AeVisita visita = new AeVisita();
			AeSitio aeSitio = new AeSitio();
			String gid = view.getGid();
			aeSitio.setGid(new Long(gid));
			visita = conv.convVisitaViewToEntity(view, aeSitio);
			List<AeVisita> visitas = actaDeEntregaDAO.getVisita(new Long(gid));
			Integer visitaCorrecta = Utils.setNumVisita(visitas.size());

			if(visitaCorrecta > 0){
				visita.setNumVisita(visitaCorrecta);
				actaDeEntregaDAO.addVisita(visita);
				return true;
			}
			else{
				return false;
			}
		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de Agregar la visita al Acta de Entrega {}", view);
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_UPDATE);
			throw aException;
		}
	}

	public void deleteActaDeEntregaByGID(String gid) throws ActaDeEntregaException {
		try{
			AeSitio aeSitio = actaDeEntregaDAO.getSitio(new Long(gid));
			actaDeEntregaDAO.deleteActaDeEntrega(aeSitio);
		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(NumberFormatException nfe){
			String msg = "El GID " + gid + " tiene un formato incorrecto";
			ActaDeEntregaException aException = new ActaDeEntregaException(msg,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_DELETE);
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de eliminar Acta de Entrega {}", gid);
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_DELETE);
			throw aException;
		}
	}

	public byte[] generarReporteById(ServletContext context, String gid) throws ActaDeEntregaException {
		byte[] reportePDF = null;
		try{
			AeSitio aeSitio = actaDeEntregaDAO.getSitio(new Long(gid));
			String tipo = aeSitio.getTipoDeFormato().trim();

			Map<String,Object> parametros = buildParametros(aeSitio, tipo);

			InputStream isReporte = null;
			if(tipo.equals("espacio")){
				isReporte = context.getResourceAsStream("/WEB-INF/classes/reportes/espacio_publico_v2_5.jrxml");
			}else{
				isReporte = context.getResourceAsStream("/WEB-INF/classes/reportes/actadeentrega_v3_6.jrxml");
			}

			JasperReport jasperReport = JasperCompileManager.compileReport(isReporte);

			reportePDF = JasperRunManager.runReportToPdf(jasperReport, parametros, new JREmptyDataSource());

		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(NumberFormatException nfe){
			String msg = "El GID " + gid + " tiene un formato incorrecto";
			ActaDeEntregaException aException = new ActaDeEntregaException(msg,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_SELECT);
			throw aException;
		}catch(Exception e){
			logger.error("Ocurrió un error al tratar de generar el Reporte {}", e);
			ActaDeEntregaException aException = new ActaDeEntregaException(e,
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_SELECT);
			throw aException;
		}
		return reportePDF;
	}

	private Map<String, Object> buildParametros(AeSitio entity, String tipo){
		Map<String,Object> parametros = Maps.newHashMap();
		// Sitio
		parametros.put("gid", entity.getGid().toString());
		parametros.put("anchoDeBanda", entity.getAnchoDeBanda());
		parametros.put("cai", entity.getCai());
		parametros.put("calle", entity.getCalle());
		parametros.put("ciudad", entity.getCiudad());
		parametros.put("colonia", entity.getColonia());
		parametros.put("cp", entity.getCp());
		parametros.put("enFunc", entity.getEnFunc());
		parametros.put("entreCalles", entity.getEntreCalles());
		parametros.put("estado", entity.getEstado());
		parametros.put("existeDisp", entity.getExisteDisp());
		parametros.put("fecha", entity.getFecha());
		parametros.put("institucion", entity.getInstitucion());
		parametros.put("latitud", entity.getLatitud());
		parametros.put("longitud", entity.getLongitud());
		parametros.put("municipio", entity.getMunicipio());
		parametros.put("nombreSitio", entity.getNombreSitio());
		parametros.put("numExt", entity.getNumExt());
		parametros.put("numInt", entity.getNumInt());
		parametros.put("tipoSitio", entity.getTipoSitio());

		Set<String> tipoSitios = Sets.newHashSet("Escuela","Centro de Salud",
				"Biblioteca","Oficina gubernamental","Plaza","Parque","U. Habitacional");

		boolean isTipoSitio = false;
		for (String tSitio : tipoSitios) {
			if(tSitio.equalsIgnoreCase(entity.getTipoSitio())){
				isTipoSitio = true;
				break;
			}
		}

		if(!isTipoSitio){
			parametros.put("otroTipoSitio", entity.getTipoSitio());
		}else{
			parametros.put("otroTipoSitio", null);
		}

		parametros.put("telefono", entity.getTelefono());
		parametros.put("tipoActa", tipo.equals("espacio")?"ESPACIO PÚBLICO":"EDIFICIO PÚBLICO");
		parametros.put("licitacion", entity.getLicitacion());

		//Informacion del sitio
		List<AeVisita> visitas = entity.getAeVisitas();
		if(!visitas.isEmpty()){
			for (AeVisita visita : visitas) {
	
				if(visita.getNumVisita()==1){
					parametros.put("vPrimeraFolio", visita.getFolioVisita());
					parametros.put("vPrimeraObservacion", visita.getObservacion());
					parametros.put("vPrimeraFecha", visita.getFecha());
				}
				else{
					if(visita.getNumVisita()==2){
						parametros.put("vSegundaFolio", visita.getFolioVisita());
						parametros.put("vSegundaObservacion", visita.getObservacion());
						parametros.put("vSegundaFecha", visita.getFecha());
					}
					else{
						if(visita.getNumVisita()==3){
							parametros.put("vTerceraFolio", visita.getFolioVisita());
							parametros.put("vTerceraObservacion", visita.getObservacion());
							parametros.put("vTerceraFecha", visita.getFecha());
						}
					}
				}
			}
		}

		//Equipos Instalados
		List<AeEquInstalado> equiposInstalados = entity.getAeEquInstalados();
		for (AeEquInstalado ei : equiposInstalados) {
			if(ei.getTipoEquipo().equals("MO")){
				parametros.put("mMarca", ei.getMarca());
				parametros.put("mModelo", ei.getModelo());
				parametros.put("mIp", ei.getIpHomo());
			}
			if(ei.getTipoEquipo().equals("PE")){
				parametros.put("peMarca", ei.getMarca());
				parametros.put("peModelo", ei.getModelo());
				parametros.put("peIp", ei.getIpHomo());
			}
			if(ei.getTipoEquipo().equals("PI")){
				parametros.put("piMarca", ei.getMarca());
				parametros.put("piModelo", ei.getModelo());
				parametros.put("piIp", ei.getIpHomo());
			}
			if(ei.getTipoEquipo().equals("FW")){
				parametros.put("fMarca", ei.getMarca());
				parametros.put("fModelo", ei.getModelo());
				parametros.put("fIp", ei.getIpHomo());
			}
		}

		//Pruebas
		AePrueba pruebas = entity.getAePrueba();
		parametros.put("catCont", pruebas.getCatCont());
		parametros.put("conectividad", pruebas.getConectividad());
		parametros.put("filtro", pruebas.getFiltro());
		parametros.put("ftpCompleto", pruebas.getFtpCompleto());
		parametros.put("portalAcceso", pruebas.getPortalAcceso());
		parametros.put("url", pruebas.getUrl());
		parametros.put("velCarga", pruebas.getVelCarga());
		parametros.put("velDesc", pruebas.getVelDesc());
		//PAI interno
		if(tipo.equals("edificio")){
			AePaiInterno paiInterno = entity.getAePaiInterno();
			parametros.put("paiiCatCont", paiInterno.getPaiiCatCont());
			parametros.put("paiiConectividad", paiInterno.getPaiiConectividad());
			parametros.put("paiiFiltro", paiInterno.getPaiiFiltro());
			parametros.put("paiiPaPassword", paiInterno.getPaiiPaPassword());
			parametros.put("paiiPassword", paiInterno.getPaiiPassword());
			parametros.put("paiiPortalAcceso", paiInterno.getPaiiPortalAcceso());
			parametros.put("paiiUrl", paiInterno.getPaiiUrl());
			parametros.put("ssid60mVisible", paiInterno.getSsid60mVisible());
		}
		//PAI externo
		AePaiExterno paiExterno = entity.getAePaiExterno();
		parametros.put("paieCatCont", paiExterno.getPaieCatCont());
		parametros.put("paieConectividad", paiExterno.getPaieConectividad());
		parametros.put("paieFiltro", paiExterno.getPaieFiltro());
		parametros.put("paiePortalAcceso", paiExterno.getPaiePortalAcceso());
		parametros.put("paiePuntoAcceso", paiExterno.getPaiePuntoAcceso());
		parametros.put("paieUrl", paiExterno.getPaieUrl());
		parametros.put("ssid250m", paiExterno.getSsid250m());
		parametros.put("ssid60m", paiExterno.getSsid60m());
		//Constatar en Sitio
		AeConstatarSitio constatar = entity.getAeConstatarSitio();
		parametros.put("entregaManual", constatar.getEntregaManual());
		parametros.put("reparacion", constatar.getReparacion());

		//Se agregan las observaciones
		List<AeObservacione> observaciones = constatar.getAeObservaciones();
		if(!observaciones.isEmpty()){
			int obsvContador = 1;
			for (AeObservacione obsv : observaciones) {
				if(obsv != null){
					parametros.put("observacion" + obsvContador, obsv.getObservacion());
					obsvContador++;
				}
			}
		}

		List<Integer> espacioImagenes = Lists.newArrayList(1,2,3,13,14,15,17,18,19,20,21,22, 23);
		//Imágenes
		for (AeImagene img : entity.getAeImagenes()) {
			if(img != null){
				if(tipo.equals("espacio") && espacioImagenes.contains(img.getPosicion())){
					parametros.put("imagen" + img.getPosicion(),
							new ByteArrayInputStream(img.getImagen()));
				}
				else{
					parametros.put("imagen" + img.getPosicion(),
							new ByteArrayInputStream(img.getImagen()));
				}
			}
		}
		return parametros;
	}

	public boolean existeActaDeEntrega(String gid) throws ActaDeEntregaException {
		try{
			int count = actaDeEntregaDAO.existeSitio(new Long(gid));
			return count > 0 ? true : false;
		}catch(ActaDeEntregaException aException){
			throw aException;
		}catch(NumberFormatException nfe){
			ActaDeEntregaException aException = new ActaDeEntregaException("El GID tiene formato inválido",
					ActaDeEntregaException.LAYER_SERVICE, ActaDeEntregaException.ACTION_SELECT);
			throw aException;
		}
	}

	public List<EquipoInstaladoView> ordenEquipos(List<EquipoInstaladoView> equipos){
		List<EquipoInstaladoView> ordenados = Lists.newArrayList();
		for(Integer i = 0;i<4;i++){
			for(EquipoInstaladoView eq : equipos){
				if(ordenados.size()==0){
					if(eq.getTipoEquipo().equalsIgnoreCase("Modem")){
						ordenados.add(eq);
					}
				}
				if(ordenados.size()==1){
					if(eq.getTipoEquipo().equalsIgnoreCase("PAI Externo")){
						ordenados.add(eq);
					}
				}
				if(ordenados.size()==2){
					if(eq.getTipoEquipo().equalsIgnoreCase("PAI Interno")){
						ordenados.add(eq);
					}
				}
				if(ordenados.size()==3){
					if(eq.getTipoEquipo().equalsIgnoreCase("Firewall")){
						ordenados.add(eq);
					}
				}
			}
			if(ordenados.size()==4){
				i=5;
			}
		}
		return ordenados;
		
	}
}
